package hello;

import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.UpdateResult;
import hello.enums.BagType;
import hello.enums.Sex;
import hello.pojos.*;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.util.*;

import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;

@SpringBootApplication
public class Application implements CommandLineRunner {

    @Autowired
    private CustomerRepository repository;

    @Autowired
    private MongoOperations mongoTemplate;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception {

        repository.deleteAll();

        // save a couple of customers
        repository.save(new Customer("Alice", "Smith", Sex.man));
        repository.save(new Customer("Bob", "Smith", Sex.woman));

        // fetch all customers
        System.out.println("Customers found with findAll():");
        System.out.println("-------------------------------");
        for (Customer customer : repository.findAll()) {
            System.out.println(customer);
        }
        System.out.println();

        // fetch an individual customer
        System.out.println("Customer found with findByFirstName('Alice'):");
        System.out.println("--------------------------------");
        System.out.println(repository.findByFirstName("Alice"));

        System.out.println("Customers found with findByLastName('Smith'):");
        System.out.println("--------------------------------");
        for (Customer customer : repository.findByLastName("Smith")) {
            System.out.println(customer);
        }

        Customer gzc = new Customer("guo", "zhicheng", Sex.man);
        final Map<BagType, Map<Long, Goods>> type_goodsGuid2Goods = gzc.getBagInfo().getType_goodsGuid2Goods();
        final Long2ObjectMap<Goods> goodsBag = new Long2ObjectOpenHashMap<>();
        final Long2ObjectMap<Goods> equipBag = new Long2ObjectOpenHashMap<>();
        type_goodsGuid2Goods.put(BagType.goodsBag, goodsBag);
        type_goodsGuid2Goods.put(BagType.equipBag, equipBag);

        goodsBag.put(100L, new Goods(1, 99, 100L));
        goodsBag.put(101L, new Goods(1, 34, 101L));

        equipBag.put(200L, new Goods(2, 123, 200L));
        equipBag.put(201L, new Goods(2, 1111, 201L));

        Mail mail1 = new Mail(1, "hello", "hello world");
        Mail mail2 = new Mail(2, "exception", "object not found");

        gzc.getMailList().add(mail1);
        gzc.getMailList().add(mail2);

        OpenServerActivityInfo openServerActivityInfo = gzc.getOpenServerActivityInfo();
        Set<Integer> awardSet = openServerActivityInfo.getAwardSet();
        awardSet.add(1000);
        awardSet.add(1005);
        awardSet.add(1007);
        awardSet.add(1007);

        repository.save(gzc);

        Customer customer = repository.findByFirstNameIgnoreCaseAndLastNameIgnoreCase("guo", "zhicheng");
//        final BagInfo bagInfo = customer.getBagInfo();
        final BagInfo bagInfo = queryPlayerBagInfo("guo", "zhicheng");

        for (Iterator<Map.Entry<BagType, Map<Long, Goods>>> it =
             bagInfo.getType_goodsGuid2Goods().entrySet().iterator(); it.hasNext(); ) {
            Map.Entry<BagType, Map<Long, Goods>> entry = it.next();
            BagType bagType = entry.getKey();
            System.out.println("----------------------" + bagType);
            Map<Long, Goods> guid2goods = entry.getValue();

            for (Goods goods : guid2goods.values()) {
                System.out.println(goods);
                System.out.println("-----------修改物品---------------");
                goods.setGoodsNum(goods.getGoodsNum() + 10000);
                System.out.println(goods);
            }
        }

        //直接写Mongo语句，加投影，只需要bagInfo字段,BasicQuery类是Query类的子类
//        BasicQuery basicQuery = new BasicQuery("{'firstName':'guo'}", "{'_id':0,'bagInfo':1}");
//        mongoTemplate.updateFirst(basicQuery, Update.update("bagInfo", bagInfo), Customer.class);


        mongoTemplate.updateFirst(query(where("firstName").is("guo")), Update.update("bagInfo", bagInfo), Customer.class);
        //直接保存玩家
//        repository.save(customer);

        //用$pull删除数组里的符合条件的多个元素，数组元素是子文档。
        {
            TestPull testPull = new TestPull();
            long curTime = System.currentTimeMillis();
            testPull.setName("lean_mongo" + curTime);

            List<TestPull.InnerClass> tags = Arrays.asList(new TestPull.InnerClass("java", 1000),
                    new TestPull.InnerClass("netty", 1001),
                    new TestPull.InnerClass("mongo", 1002),
                    new TestPull.InnerClass("redis", 1003));
            testPull.setTags(tags);
            mongoTemplate.save(testPull);

            Query removeQuery = Query.query(Criteria.where("name").is("lean_mongo" + curTime));
            UpdateResult wc = mongoTemplate.upsert(removeQuery,
                    //删除一个符合条件的数组元素
                    new Update().pull("tags", Query.query(Criteria.where("tag").is("java"))),
                    TestPull.class);
            System.out.println(String.format("ModifiedCount=[%s],Acknowledged=[%s]", wc.getModifiedCount(), wc.wasAcknowledged()));

            UpdateResult wc1 = mongoTemplate.upsert(removeQuery,
                    //删除多个个符合条件的数组元素
                    new Update().pull("tags", Query.query(Criteria.where("tag").in(Arrays.asList("mongo", "netty")))),
                    TestPull.class);
            System.out.println(String.format("ModifiedCount=[%s],Acknowledged=[%s]", wc1.getModifiedCount(), wc1.wasAcknowledged()));
        }

        //mongo3.6新加的$[<identifier>]修改器Update函数的arrayFilters参数选项，一次修改多个数组元素。
        {
            Student student = new Student(Arrays.asList(new Student.InnerClass(1, 100, 1),
                    new Student.InnerClass(2, 145, 4),
                    new Student.InnerClass(3, 170, 3)));
            mongoTemplate.save(student);

            //先直接用原生JAVA驱动去拼的Mongo语法
            //db.getCollection("student").update({},{$set:{"grades.$[element].mean":100}},{multi:true,arrayFilters:[{"element.mean":{$gt:100}}]});
            MongoTemplate template = (MongoTemplate) mongoTemplate;
            template.getDb().getCollection("student", Student.class).
                    updateMany(new Document(),
                            new Document("$set", new Document("grades.$[element].mean", 100)),
                            new UpdateOptions().arrayFilters(Collections.singletonList(
                                    new Document("element.mean", new Document("$gt", 100)))));

            //TODO Spring data mongo的写法暂时不知道怎么写
//            WriteResult wr = mongoTemplate.updateMulti(new Query(Criteria.where("_id").is(1)),
//                    new Update().set("grades.$[item]", 100),
//                    new UpdateOptions().arrayFilters(Arrays.asList(Filters.gt("item", 100))),
//                    Student.class
//            );
//            System.out.println(String.format("WriteResult=[%s]", wr.toString()));
        }
    }

    private BagInfo queryPlayerBagInfo(String firstName, String lastName) {
        Query query = query(where("firstName").is(firstName).andOperator(where("lastName").is(lastName)));
        Customer customer = mongoTemplate.findOne(query, Customer.class);
        return customer.getBagInfo();
    }

}
